#include "root.h"
#include "lex.h"
#include "parse.h"
#include "exec.h"

void tree_execute_redirect(tree_t *this)
{
    tree_t *body = tree_get_child(this, 0);
    tree_t *operator = tree_get_child(this, 1);
    tree_t *file = tree_get_child(this, 2);
    char *path;
    int fd, redirect_fd;

    path = file->token;
    if(token_is(operator, "<")) {
        fd = open(path, O_RDONLY);
        redirect_fd = 0;
    }
    else if(token_is(operator, ">")) {
        fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 0777);
        redirect_fd = 1;
    }
    else if(token_is(operator, ">>")) {
        fd = open(path, O_RDWR | O_APPEND | O_CREAT, 0777);
        redirect_fd = 1;
    }

    dup2(fd, redirect_fd);
    close(fd);
    tree_execute(body);
}

#define MAX_ARGC 16
void tree_execute_basic(tree_t *this)
{
    int n;
    tree_t *child;
    char *args[MAX_ARGC];
    vector_each (&this->child_vector, n, child)
        args[n] = child->token;

    args[n] = NULL;
    execvp(args[0], args);
}

void tree_execute_pipe(tree_t *this)
{
    int fd[2];
    tree_t *left = tree_get_child(this, 0);
    tree_t *right = tree_get_child(this, 1);

    pipe(fd);
    if(fork() == 0) {
        close(1);
        dup(fd[1]);
        close(fd[0]);
        close(fd[1]);
        tree_execute(left);
    } else {
        close(0);
        dup(fd[0]);
        close(fd[0]);
        close(fd[1]);
        tree_execute(right);
    }
}

int tree_execute_builtin(tree_t *this)
{
    int flag = 0;
    if(this->type == TREE_BASIC) {
        tree_t *child0 = tree_get_child(this, 0);
        if(token_is(child0, "exit")) {
            flag = 1;
            exit(0);
        }
        if(token_is(child0, "pwd")) {
            flag = 1;
            char buf[1024];
            getcwd(buf, 1024);
            printf("%s\n", buf);
        }
        if(token_is(child0, "cd")) {
            flag = 1;
            if(this->child_vector.count == 1) return flag;
            char buf[1024];
            getcwd(buf, 1024);
            strcat(buf, "/");
            tree_t *child1 = tree_get_child(this, 1);
            strcat(buf, child1->token);
            chdir(buf);
        }
    }
    return flag;
}

void tree_execute_async(tree_t *this)
{
    tree_execute(tree_get_child(this, 0));
}

void tree_execute(tree_t *this)
{
    switch (this->type) {
        case TREE_ASYNC:
            
            tree_execute_async(this); 
            break;

        case TREE_PIPE:
            tree_execute_pipe(this); 
            break;

        case TREE_REDIRICT:
            tree_execute_redirect(this); 
            break;

        case TREE_BASIC:
            tree_execute_basic(this); 
            break;
    }
}

void tree_execute_wrapper(tree_t *this)
{
    if (tree_execute_builtin(this))
        return;
    pid_t pid = fork();
    if (pid == 0) {
        tree_execute(this);
    }
   
    // cc a-large-file.c &
    if (this->type != TREE_ASYNC)
        wait(NULL);
}
